package com.security.manage.search.controller;

import com.alibaba.fastjson.JSONObject;
import com.security.manage.search.model.*;
import com.security.manage.search.repository.*;
import com.security.manage.search.service.UserMicroService;
import org.apache.commons.lang.StringUtils;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.sort.SortBuilders;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import java.lang.reflect.Field;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;

@RestController
public class SearchController {

    @Resource
    private LogRepository logRepository;

    @Resource
    private SoftInstallInfoRepository softInstallInfoRepository;

    @Resource
    private RegistryInfoRepository registryInfoRepository;

    @Resource
    private FireWalInfoRepository fireWalInfoRepository;

    @Resource
    private BrowserBookmarkRepository browserBookmarkRepository;

    @Resource
    private BrowserDownloadRepository browserDownloadRepository;

    @Resource
    private BrowserRecordRepository browserRecordRepository;

    @Resource
    private UserMicroService userMicroService;

    private DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");

    @GetMapping("/log/list")
    public ResData search(SearchDto dto){


        if (StringUtils.isEmpty(dto.getEnd())){
            dto.setEnd(LocalDateTime.now().format(dateTimeFormatter));
        }

        if ("Software".equals(dto.getType())){
            return getSoftInfos(dto);
        }

        if ("Registry".equals(dto.getType())){
            return getRegistryInfos(dto);
        }

        if ("Firewall".equals(dto.getType())){
            return getFirewallInfos(dto);
        }

        if (dto.getType().startsWith("Browser")||dto.getType().endsWith("Browser")){
            return getBrowserInfos(dto);
        }

        NativeSearchQueryBuilder nativeSearchQueryBuilder = new NativeSearchQueryBuilder();
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        if (StringUtils.isNotEmpty(dto.getKeyWord())) {
            boolQueryBuilder.must(QueryBuilders.matchQuery("description", dto.getKeyWord()));
        }

        if (dto.getStart() != null) {
            boolQueryBuilder.must(QueryBuilders.rangeQuery("recordTime").gte(dto.getStart()).lte(dto.getEnd()));
        }else {
            boolQueryBuilder.must(QueryBuilders.rangeQuery("recordTime").lte(dto.getEnd()));
        }

        if (dto.getUserId() != null && dto.getUserId()!=0L) {
            boolQueryBuilder.must(QueryBuilders.termQuery("userId",dto.getUserId()));
        }

        if (StringUtils.isNotEmpty(dto.getDeptIds())) {
            String[] idArray = StringUtils.split(dto.getDeptIds(), ",");
            List<Long> ids = Arrays.stream(idArray)
                    .map(s -> Long.parseLong(s.trim())).collect(Collectors.toList());
            boolQueryBuilder.must(QueryBuilders.termsQuery("deptId",ids));
        }

        if (StringUtils.isNotEmpty(dto.getType())) {
            boolQueryBuilder.must(QueryBuilders.matchPhraseQuery("logName",dto.getType()));
        }

        nativeSearchQueryBuilder.withQuery(boolQueryBuilder);
        nativeSearchQueryBuilder.withPageable(PageRequest.of(dto.getPage(),dto.getSize()));
        nativeSearchQueryBuilder.withSort(SortBuilders.fieldSort("recordTime").order(SortOrder.DESC));
        Page<Log> pageInfos = logRepository.search(nativeSearchQueryBuilder.build());
        List<Log> logStream = pageInfos.get().collect(Collectors.toList());
        String data = userMicroService.listAll();
        JSONObject jsonObject = JSONObject.parseObject(data);
        JSONObject users = jsonObject.getJSONObject("users");
        JSONObject depts = jsonObject.getJSONObject("depts");

        List<Map<String,Object>> resultList = new ArrayList<>();
        logStream.forEach(log -> {
            if (StringUtils.isNotEmpty(dto.getType())){
                log.setLogName(dto.getType());
            }else{
                String logName = log.getLogName();
                if (logName.contains(",")){
                   log.setLogName(logName.substring(0,logName.indexOf(",")));
                }
            }
            Map<String,Object> map = objectToMap(log);
            map.put("userName",users.get(map.get("userId")));
            map.put("deptName",depts.get(map.get("deptId")));
            resultList.add(map);
        });
        Map<String,Object> resultMap = new HashMap<>();
        resultMap.put("count",pageInfos.getTotalElements());
        resultMap.put("data",resultList);

        return new ResData(1,"查询成功",resultMap);
    }

    private ResData getBrowserInfos(SearchDto dto) {
        int page = dto.getPage();
        int size = dto.getSize();
        Sort sort = Sort.by(Sort.Direction.DESC, "id");
        PageRequest of = PageRequest.of(page, size, sort);
        Page all = null;
        if (dto.getType().contains("record") || dto.getType().contains("Pan") || dto.getType().contains("Mail")){
            Specification<BrowserRecord> specification = new Specification<BrowserRecord>() {
                @Override
                public Predicate toPredicate(Root<BrowserRecord> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) {
                    List<Predicate> predicateList = new ArrayList<>();

                    if (StringUtils.isNotEmpty(dto.getDeptIds())) {
                        CriteriaBuilder.In<Long> in = criteriaBuilder.in(root.get("deptId").as(Long.class));
                        for (String id : dto.getDeptIds().split(",")) {
                            in.value(Long.parseLong(id));
                        }
                        predicateList.add(in);
                    }

                    if (dto.getUserId() != null && dto.getUserId() != 0L) {
                        predicateList.add(criteriaBuilder.equal(root.get("userId").as(Long.class), dto.getUserId()));
                    }

                    if (dto.getType().contains("Pan")){
                        predicateList.add(criteriaBuilder.equal(root.get("panFlag").as(Integer.class),1));
                    }

                    if (dto.getType().contains("Mail")){
                        predicateList.add(criteriaBuilder.equal(root.get("mailFlag").as(Integer.class),1));
                    }

                    Predicate[] pre = new Predicate[predicateList.size()];
                    pre = predicateList.toArray(pre);
                    return criteriaQuery.where(pre).getRestriction();
                }
            };
            all = browserRecordRepository.findAll(specification, of);
        }
        if (dto.getType().contains("bookmark")){
            Specification<BrowserBookmark> specification = new Specification<BrowserBookmark>() {
                @Override
                public Predicate toPredicate(Root<BrowserBookmark> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) {
                    List<Predicate> predicateList = new ArrayList<>();

                    if (StringUtils.isNotEmpty(dto.getDeptIds())) {
                        CriteriaBuilder.In<Long> in = criteriaBuilder.in(root.get("deptId").as(Long.class));
                        for (String id : dto.getDeptIds().split(",")) {
                            in.value(Long.parseLong(id));
                        }
                        predicateList.add(in);
                    }

                    if (dto.getUserId() != null && dto.getUserId() != 0L) {
                        predicateList.add(criteriaBuilder.equal(root.get("userId").as(Long.class), dto.getUserId()));
                    }

                    Predicate[] pre = new Predicate[predicateList.size()];
                    pre = predicateList.toArray(pre);
                    return criteriaQuery.where(pre).getRestriction();
                }
            };
            all = browserBookmarkRepository.findAll(specification, of);
        }
        if(dto.getType().contains("download")){
            Specification<BrowserDownload> specification = new Specification<BrowserDownload>() {
                @Override
                public Predicate toPredicate(Root<BrowserDownload> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) {
                    List<Predicate> predicateList = new ArrayList<>();

                    if (StringUtils.isNotEmpty(dto.getDeptIds())) {
                        CriteriaBuilder.In<Long> in = criteriaBuilder.in(root.get("deptId").as(Long.class));
                        for (String id : dto.getDeptIds().split(",")) {
                            in.value(Long.parseLong(id));
                        }
                        predicateList.add(in);
                    }

                    if (dto.getUserId() != null && dto.getUserId() != 0L) {
                        predicateList.add(criteriaBuilder.equal(root.get("userId").as(Long.class), dto.getUserId()));
                    }

                    Predicate[] pre = new Predicate[predicateList.size()];
                    pre = predicateList.toArray(pre);
                    return criteriaQuery.where(pre).getRestriction();
                }
            };
            all = browserDownloadRepository.findAll(specification, of);
        }
        if (all==null){
            return new ResData(0,"类型参数错误",null);
        }

        String data = userMicroService.listAll();
        JSONObject jsonObject = JSONObject.parseObject(data);
        JSONObject users = jsonObject.getJSONObject("users");
        JSONObject depts = jsonObject.getJSONObject("depts");

        List<Map<String,Object>> result = new ArrayList<>();
        all.get().forEach(info -> {
            Map<String,Object> map = objectToMap(info);
            map.put("logName",dto.getType());
            map.put("userName",users.get(map.get("userId")));
            map.put("deptName",depts.get(map.get("deptId")));
            result.add(map);
        });

        Map<String, Object> map = new HashMap<>();
        map.put("count", all.getTotalElements());
        map.put("data", result);
        return new ResData(1, "获取成功", map);
    }

    public Map<String, Object> objectToMap(Object obj) {
        if (obj == null) {
            return null;
        }
        Map<String, Object> map = new HashMap<String, Object>();
        try {
            Field[] declaredFields = obj.getClass().getDeclaredFields();
            for (Field field : declaredFields) {
                field.setAccessible(true);
                map.put(field.getName(), field.get(obj));
            }
        }catch (Exception e){

        }

        return map;
    }

    private ResData getFirewallInfos(SearchDto dto) {
        NativeSearchQueryBuilder nativeSearchQueryBuilder = new NativeSearchQueryBuilder();
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        if (StringUtils.isNotEmpty(dto.getDeptIds())) {
            String[] idArray = StringUtils.split(dto.getDeptIds(), ",");
            List<Long> ids = Arrays.stream(idArray)
                    .map(s -> Long.parseLong(s.trim())).collect(Collectors.toList());
            boolQueryBuilder.must(QueryBuilders.termsQuery("deptId",ids));
        }

        if (dto.getUserId() != null && dto.getUserId()!=0L) {
            boolQueryBuilder.must(QueryBuilders.termQuery("userId",dto.getUserId()));
        }

        if (StringUtils.isEmpty(dto.getEnd())){
            dto.setEnd(LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
        }

        if (dto.getStart() != null) {
            boolQueryBuilder.must(QueryBuilders.rangeQuery("dateTime").gte(dto.getStart()).lte(dto.getEnd()));
        }else {
            boolQueryBuilder.must(QueryBuilders.rangeQuery("dateTime").lte(dto.getEnd()));
        }

        nativeSearchQueryBuilder.withQuery(boolQueryBuilder);
        nativeSearchQueryBuilder.withPageable(PageRequest.of(dto.getPage(),dto.getSize()));
        nativeSearchQueryBuilder.withSort(SortBuilders.fieldSort("dateTime").order(SortOrder.DESC));
        Page<FireWallInfo> pageInfos = fireWalInfoRepository.search(nativeSearchQueryBuilder.build());
        String data = userMicroService.listAll();
        JSONObject jsonObject = JSONObject.parseObject(data);
        JSONObject users = jsonObject.getJSONObject("users");
        JSONObject depts = jsonObject.getJSONObject("depts");
        List<Map<String,Object>> result = new ArrayList<>();
        pageInfos.get().forEach(info -> {
            Map<String,Object> map = objectToMap(info);
            map.put("logName",dto.getType());
            map.put("userName",users.get(map.get("userId")));
            map.put("deptName",depts.get(map.get("deptId")));
            result.add(map);
        });

        Map<String,Object> resultMap = new HashMap<>();
        resultMap.put("count",pageInfos.getTotalElements());
        resultMap.put("data",result);

        return new ResData(1,"获取成功",resultMap);
    }

    private ResData getRegistryInfos(SearchDto dto) {
        int page = dto.getPage();
        int size = dto.getSize();
        Sort sort = Sort.by(Sort.Direction.DESC, "id");
        PageRequest of = PageRequest.of(page, size, sort);
        Specification<RegistryInfo> specification = new Specification<RegistryInfo>() {
            @Override
            public Predicate toPredicate(Root<RegistryInfo> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) {
                List<Predicate> predicateList = new ArrayList<>();

                if (StringUtils.isNotEmpty(dto.getDeptIds())) {
                    CriteriaBuilder.In<Long> in = criteriaBuilder.in(root.get("deptId").as(Long.class));
                    for (String id : dto.getDeptIds().split(",")) {
                        in.value(Long.parseLong(id));
                    }
                    predicateList.add(in);
                }

                if (dto.getUserId() != null && dto.getUserId() != 0L) {
                    predicateList.add(criteriaBuilder.equal(root.get("userId").as(Long.class), dto.getUserId()));
                }

                Predicate[] pre = new Predicate[predicateList.size()];
                pre = predicateList.toArray(pre);
                return criteriaQuery.where(pre).getRestriction();
            }
        };
        Page<RegistryInfo> all = registryInfoRepository.findAll(specification, of);
        String data = userMicroService.listAll();
        JSONObject jsonObject = JSONObject.parseObject(data);
        JSONObject users = jsonObject.getJSONObject("users");
        JSONObject depts = jsonObject.getJSONObject("depts");

        List<Map<String,Object>> result = new ArrayList<>();
        all.get().forEach(info -> {
            Map<String,Object> map = objectToMap(info);
            map.put("logName",dto.getType());
            map.put("userName",users.get(map.get("userId")));
            map.put("deptName",depts.get(map.get("deptId")));
            result.add(map);
        });
        Map<String, Object> map = new HashMap<>();
        map.put("count", all.getTotalElements());
        map.put("data", result);
        return new ResData(1, "获取成功", map);
    }

    private ResData getSoftInfos(SearchDto dto) {
        int page = dto.getPage();
        int size = dto.getSize();
        Sort sort = Sort.by(Sort.Direction.DESC, "installDate");
        PageRequest of = PageRequest.of(page, size, sort);
        Specification<SoftInstallInfo> specification = new Specification<SoftInstallInfo>() {
            @Override
            public Predicate toPredicate(Root<SoftInstallInfo> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) {
                List<Predicate> predicateList = new ArrayList<>();

                if (StringUtils.isNotEmpty(dto.getDeptIds())) {
                    CriteriaBuilder.In<Long> in = criteriaBuilder.in(root.get("deptId").as(Long.class));
                    for (String id : dto.getDeptIds().split(",")) {
                        in.value(Long.parseLong(id));
                    }
                    predicateList.add(in);
                }

                if (dto.getUserId() != null && dto.getUserId() != 0L) {
                    predicateList.add(criteriaBuilder.equal(root.get("userId").as(Long.class), dto.getUserId()));
                }

                LocalDateTime endTime = LocalDateTime.parse(dto.getEnd(), dateTimeFormatter);
                if (StringUtils.isNotEmpty(dto.getStart())){
                    LocalDateTime startTime = LocalDateTime.parse(dto.getStart(), dateTimeFormatter);
                    predicateList.add(criteriaBuilder.between(root.get("installDate").as(LocalDateTime.class),startTime,endTime));
                }else {
                    predicateList.add(criteriaBuilder.lessThanOrEqualTo(root.get("installDate").as(LocalDateTime.class),endTime));
                }

                Predicate[] pre = new Predicate[predicateList.size()];
                pre = predicateList.toArray(pre);
                return criteriaQuery.where(pre).getRestriction();
            }
        };

        Page<SoftInstallInfo> all = softInstallInfoRepository.findAll(specification, of);
        String data = userMicroService.listAll();
        JSONObject jsonObject = JSONObject.parseObject(data);
        JSONObject users = jsonObject.getJSONObject("users");
        JSONObject depts = jsonObject.getJSONObject("depts");

        List<Map<String,Object>> result = new ArrayList<>();
        all.get().forEach(info -> {
            Map<String,Object> map = objectToMap(info);
            map.put("logName",dto.getType());
            map.put("userName",users.get(map.get("userId")));
            map.put("deptName",depts.get(map.get("deptId")));
            result.add(map);
        });
        Map<String, Object> map = new HashMap<>();
        map.put("count", all.getTotalElements());
        map.put("data", result);
        return new ResData(1, "获取成功", map);
    }

}
